1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.webmacro.directive;
24
25 /***
26 * This directive allows the instantiation of objects in WMScript.
27 */
28
29 import org.webmacro.*;
30 import org.webmacro.engine.*;
31 import org.webmacro.util.Instantiator;
32
33 import java.io.IOException;
34 import java.util.HashMap;
35 import java.util.Map;
36 /***
37 * Implements a directive allowing a script writer
38 * to instantiate an arbitrary
39 * java object in a WebMacro template subject to
40 * security restrictions.
41 */
42
43 public class BeanDirective extends Directive
44 {
45
46 private static final String APP_BEANS_KEY = "org.webmacro.directive.BeanDirective.appBeans";
47
48 private static final int BEAN_TARGET = 1;
49
50 private static final int BEAN_CLASS_NAME = 2;
51
52 private static final int BEAN_SCOPE = 3;
53
54 private static final int BEAN_SCOPE_GLOBAL = 4;
55
56 private static final int BEAN_SCOPE_APPLICATION = 5;
57
58 private static final int BEAN_SCOPE_SESSION = 6;
59
60 private static final int BEAN_SCOPE_PAGE = 7;
61
62 private static final int BEAN_INITARGS = 8;
63
64 private static final int BEAN_INITARGS_VAL = 9;
65
66 private static final int BEAN_TYPE_STATIC = 10;
67
68 private static final int BEAN_ON_NEW = 11;
69
70 private static final int BEAN_ON_NEW_BLOCK = 12;
71
72 private static final UndefinedMacro UNDEF = UndefinedMacro.getInstance();
73
74 private static int[] BEAN_SCOPES =
75 { BEAN_SCOPE_GLOBAL, BEAN_SCOPE_APPLICATION, BEAN_SCOPE_SESSION,
76 BEAN_SCOPE_PAGE };
77
78 static Map globalBeans = new HashMap(20);
79
80 private Variable target;
81
82 private String targetName;
83
84 private String _className;
85
86 private int scope;
87
88 private boolean isStaticClass;
89
90 private Object initArgObj;
91
92 private Object[] initArgs;
93
94 private Block onNewBlock;
95
96 private Log _log;
97
98 private Broker _broker;
99
100 private static final ArgDescriptor[] myArgs = new ArgDescriptor[]
101 { new LValueArg(BEAN_TARGET), new AssignmentArg(),
102 new QuotedStringArg(BEAN_CLASS_NAME), new OptionalGroup(3),
103 new KeywordArg(BEAN_SCOPE, "scope"), new AssignmentArg(),
104 new SingleOptionChoice(5), new OptionalGroup(1),
105 new KeywordArg(BEAN_SCOPE_GLOBAL, "global"), new OptionalGroup(1),
106 new KeywordArg(BEAN_SCOPE_APPLICATION, "application"),
107 new OptionalGroup(1),
108 new KeywordArg(BEAN_SCOPE_SESSION, "session"),
109 new OptionalGroup(1), new KeywordArg(BEAN_SCOPE_PAGE, "page"),
110 new OptionalGroup(1), new KeywordArg(BEAN_TYPE_STATIC, "static"),
111 new OptionalGroup(2), new KeywordArg(BEAN_INITARGS, "initArgs"),
112 new RValueArg(BEAN_INITARGS_VAL), new OptionalGroup(2),
113 new KeywordArg(BEAN_ON_NEW, "onNew"),
114 new BlockArg(BEAN_ON_NEW_BLOCK) };
115
116 private static final DirectiveDescriptor myDescr = new DirectiveDescriptor(
117 "bean", null, myArgs, null);
118
119 public static DirectiveDescriptor getDescriptor()
120 {
121 return myDescr;
122 }
123
124 public BeanDirective()
125 {
126 }
127
128 public Object build(DirectiveBuilder builder, BuildContext bc)
129 throws BuildException
130 {
131 _broker = bc.getBroker();
132 _log = _broker.getLog("directive");
133
134
135 try
136 {
137 target = (Variable) builder.getArg(BEAN_TARGET, bc);
138 }
139 catch (ClassCastException e)
140 {
141 throw new NotVariableBuildException(myDescr.name, e);
142 }
143 targetName = target.getName();
144 _className = (String) builder.getArg(BEAN_CLASS_NAME, bc);
145 classForName(_className);
146
147
148
149 isStaticClass = (builder.getArg(BEAN_TYPE_STATIC) != null);
150 if (isStaticClass)
151 scope = BEAN_SCOPE_GLOBAL;
152 else
153 {
154 scope = getScope(builder, bc);
155
156 initArgObj = builder.getArg(BEAN_INITARGS_VAL);
157 if (initArgObj instanceof Builder)
158 initArgObj = ((Builder) initArgObj).build(bc);
159 }
160 onNewBlock = (Block) builder.getArg(BEAN_ON_NEW_BLOCK, bc);
161
162 _log.debug("BeanDirective, target=" + target + ", className="
163 + _className + ", scope=" + scope + ", isStaticClass="
164 + isStaticClass + ", initArgs=" + initArgObj);
165 return this;
166 }
167
168 public void write(FastWriter out, Context context) throws PropertyException,
169 IOException
170 {
171 Map appBeans = (Map) _broker.getBrokerLocal(APP_BEANS_KEY);
172
173
174 boolean isNew = false;
175
176 try
177 {
178 while (initArgObj instanceof Macro && initArgObj != UNDEF)
179 initArgObj = ((Macro) initArgObj).evaluate(context);
180
181
182 if (initArgObj == null || initArgObj.getClass().isArray())
183 {
184 initArgs = (Object[]) initArgObj;
185 }
186 else
187 {
188 initArgs = new Object[]
189 { initArgObj };
190 }
191
192 Object o = null;
193 Class c = null;
194 switch (scope)
195 {
196 case BEAN_SCOPE_GLOBAL:
197 synchronized (globalBeans)
198 {
199 o = globalBeans.get(targetName);
200 if (o == null)
201 {
202 if (isStaticClass)
203 {
204 c = context.getBroker().classForName(_className);
205 o = new org.webmacro.engine.StaticClassWrapper(c);
206 }
207 else
208 {
209
210
211 o = instantiate(_className, initArgs);
212 }
213 isNew = true;
214 globalBeans.put(targetName, o);
215 }
216 }
217 break;
218
219 case BEAN_SCOPE_APPLICATION:
220 synchronized (appBeans)
221 {
222 o = appBeans.get(targetName);
223 if (o == null)
224 {
225 o = instantiate(_className, initArgs);
226 isNew = true;
227 appBeans.put(targetName, o);
228 }
229 }
230 break;
231
232 case BEAN_SCOPE_SESSION:
233 javax.servlet.http.HttpSession session = (javax.servlet.http.HttpSession) context
234 .getProperty("Session");
235
236 if (session != null)
237 {
238 synchronized (session)
239 {
240 o = session.getAttribute(targetName);
241 if (o == null)
242 {
243 o = instantiate(_className, initArgs);
244 isNew = true;
245 session.setAttribute(targetName, o);
246 }
247 }
248 }
249 else
250 {
251 PropertyException e = new PropertyException(
252 "#bean usage error: session scope is only valid with servlets!");
253 _broker.getEvaluationExceptionHandler().evaluate(target,
254 context, e);
255 }
256 break;
257 default:
258
259
260
261
262 o = instantiate(_className, initArgs);
263 isNew = true;
264 if (o != null)
265 {
266 Class[] paramTypes =
267 { Context.class };
268 try
269 {
270 java.lang.reflect.Method m = o.getClass().getMethod(
271 "init", paramTypes);
272 if (m != null)
273 {
274 Object[] args =
275 { context };
276 m.invoke(o, args);
277 }
278 }
279 catch (Exception e)
280 {
281 }
282 }
283 break;
284 }
285
286 _log.debug("BeanDirective: Class " + _className + " loaded.");
287 target.setValue(context, o);
288 }
289 catch (PropertyException e)
290 {
291 this._broker.getEvaluationExceptionHandler().evaluate(target, context,
292 e);
293 }
294 catch (Exception e)
295 {
296 String errorText = "BeanDirective: Unable to load bean " + target
297 + " of type " + _className;
298 writeWarning(errorText, context, out);
299 }
300 if (isNew && onNewBlock != null)
301 onNewBlock.write(out, context);
302
303 }
304
305 public void accept(TemplateVisitor v)
306 {
307 v.beginDirective(myDescr.name);
308 v.visitDirectiveArg("BeanTarget", target);
309 v.visitDirectiveArg("BeanClass", _className);
310 v.visitDirectiveArg("BeanScope", new Integer(scope));
311 v.visitDirectiveArg("BeanIsStatic", new Boolean(isStaticClass));
312 v.visitDirectiveArg("BeanInitArgs", initArgs);
313 v.endDirective();
314 }
315
316 private Object instantiate(String className, Object[] args) throws Exception
317 {
318 Class c = classForName(className);
319 return instantiate(c, args);
320 }
321
322 private Object instantiate(Class c, Object[] args) throws Exception
323 {
324 return Instantiator.getInstance(_broker).instantiate(c, args);
325 }
326
327 private static int getScope(DirectiveBuilder builder, BuildContext bc)
328 throws org.webmacro.engine.BuildException
329 {
330 int scope = -1;
331
332 for (int i = 0; i < BEAN_SCOPES.length; i++)
333 {
334 scope = BEAN_SCOPES[i];
335 if (builder.getArg(scope) != null)
336 break;
337 }
338 return scope;
339 }
340
341 public static void init(Broker b)
342 {
343
344 synchronized (b)
345 {
346 b.setBrokerLocal(APP_BEANS_KEY, new HashMap(20));
347 }
348 }
349
350 private Class classForName(String className) throws BuildException
351 {
352
353 Class c;
354 try
355 {
356 c = Instantiator.getInstance(_broker).classForName(className);
357 }
358 catch (WebMacroException e)
359 {
360 throw new BuildException("BeanDirective failed to load class \""
361 + className + "\"", e);
362 }
363 return c;
364 }
365 }